/************************************************************************

								Heroes IV
					  Copyright 2001, The 3DO Company

	------------------------------------------------------------------
  					       war_institute.cpp

	$Header: /game/war_institute.cpp $

	$NoKeywords: $

 ************************************************************************/
// kill the 255+ character identifier warning.  Templates tend to exceed that easily.
#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "war_institute.h"

#include "adventure_frame.h"
#include "adventure_map.h"
#include "adventure_sounds.h"
#include "ai_value.h"
#include "army.h"
#include "basic_dialog.h"
#include "bitmap_group.h"
#include "bitmap_group_cache.h"
#include "bitmap_layer_window.h"
#include "creature_array.h"
#include "creature_icon_window.h"
#include "creature_stack.h"
#include "dialog_war_institute.h"
#include "elements_of.h"
#include "external_string.h"
#include "hero.h"
#include "hero_ptr.h"
#include "material_array.h"
#include "mouse_event.h"
#include "music.h"
#include "object_registration.h"
#include "player.h"
#include "popup_window.h"
#include "random.h"
#include "replace_keywords.h"
#include "simple_dialog.h"
#include "skill.h"
#include "skill_icon.h"
#include "skill_properties.h"
#include "skill_set.h"
#include "standard_fonts.h"
#include "text_window.h"

extern t_external_string const k_text_dead_heroes_ineligible;

namespace
{
	int const k_current_map_format_version = 1;
	t_object_registration<t_war_institute> k_registration( k_adv_object_school, 
														   k_school_war_institute );

	t_bitmap_group_cache const	k_war_institute_bitmaps( "dialog.war_institute" );

	const int k_skill_cost = 2000;
}

// -------------------------------------------------------------
// Selected hero gains skill from visiting a war institute
// -------------------------------------------------------------
t_war_institute::t_war_institute( std::string const& source )
         : t_single_use_object( source ),
		   m_available_skills(::get_skill_set(true, true, false, true))
{
}

// -------------------------------------------------------------
// -------------------------------------------------------------
t_war_institute::t_war_institute( t_skill_set const& available_skills, 
								  std::string const& model_name )
         : t_single_use_object( model_name ),
		   m_available_skills(available_skills)
{
}
	

// -------------------------------------------------------
// Show skill upgrade aquired by hero.
// -------------------------------------------------------
static void show_upgraded_hero_skill( t_hero* hero_ptr, t_skill skill,
									  t_basic_dialog* dialog_ptr )
{
	t_window*			parent;
	t_screen_rect       rect;
	t_skill_icon*       skill_icon_ptr;
	t_screen_point      center;
    
	// Create invisible parent to house 2 icons.
	parent = new t_window( t_screen_rect(0,0,0,0), t_window::k_completely_transparent, dialog_ptr );

	// Create and add in the skill icon
    skill_icon_ptr = new t_skill_icon( skill, t_screen_rect(0, 0, 40, 52), parent );

	center.x = skill_icon_ptr->get_width() / 2;
	center.y = skill_icon_ptr->get_height();

	center.y += 5;

    // Add the skill text.
	t_text_window* text_window;
	t_string_vector wrapped_text;
	std::string     text = ::get_name(skill);
	t_cached_font   c_font = get_font(16);

    // Set skill text rectangle.
	rect.left = 0;
	rect.top = 0;
	rect.right = c_font->wrap_text( wrapped_text, text.c_str(), 50, 100); //160 );
	rect.bottom = wrapped_text.size() * c_font->get_height();
	
	rect -= t_screen_point( rect.right / 2, 0 );
    rect += center;

	text_window = new t_text_window( c_font, rect, parent, text, t_pixel_24(0,0,0));
	text_window->set_center_horizontal();
	text_window->set_drop_shadow( false );
		     
	/*
    ** Add the hero portrait window
    */
	t_window* window;

	//center.y = text_window.bottom + 5;
	center.y += 45;

	rect = t_screen_rect(0,0,52,52);
	window = new t_creature_icon_window( rect, parent, hero_ptr );

	rect = window->get_client_rect();
	rect -= t_screen_point( rect.width() / 2, 0 );
	rect += center;
	window->move( rect );
	parent->set_size_from_children();
	//dialog_ptr->add_display_window( parent, ::get_name( skill ) );
	dialog_ptr->add_display_window( parent, hero_ptr->get_name() );
}

void t_war_institute::activate_trigger( t_army* army, t_adv_map_point const& point, 
		                                t_direction direction, t_adventure_frame* frame )
{
	if (army->get_owner_number() < 0)
		return;
	
	// AI decider
	t_player* player = army->get_owner();
	if (player->is_computer()) 
	{
		for (int i = 0; i < t_creature_array::k_size; i++)
		{
			if (!player_can_afford( army, k_skill_cost)) 
				return;  

			t_hero* hero = (*army)[i].get_hero();

			if (!hero || hero->has_visited( *this ) || (ai_value_to_hero( hero, army ) <= 0.0f))
				continue;

			std::vector<t_skill> upgradable_skills = get_upgradable_skills(hero);
			int index = random( 0, (upgradable_skills.size() - 1) );
    		hero->learn_skill ( upgradable_skills[index] );
			hero->update_class();
			hero->set_visited( *this );
		}

		return;
	}

	int                     i;
	t_hero*                 hero_ptr;
	std::vector<t_hero *>   heroes;
	std::vector<t_hero *>   selectable_heroes;
	t_creature_array&       creatures = army->get_creatures();
	std::vector<t_skill>    upgradable_skills;
	std::string             text;
    int                     random_skill_index = 0;
	bool                    dead_heroes = true;
	bool                    any_heroes = false;

    /*
    ** Check for eligible heroes.
    */
	for (i = 0; i < t_creature_array::k_size; i++)
	{
		hero_ptr = creatures[i].get_hero();

        // Hero or not?        
		if (hero_ptr == 0)
			continue;
	    
        any_heroes = true;
		
        if (hero_ptr->is_dead())
			continue;
	    dead_heroes = false;

		heroes.push_back( hero_ptr );

        // Has the hero visited?
		if (hero_ptr->has_visited( *this ))
			continue;

        if (has_upgradable_skills(hero_ptr))
			selectable_heroes.push_back(hero_ptr);
	}

    /*
    ** If heroes aren't eligible for upgrade, display appropiate dialog.
    */
	t_sound_cache music = get_music_playing();
	stop_music();
	get_dialog_sound( k_dialog_sound_military )->play( get_sound_volume() );

    if (selectable_heroes.empty())
    {
	    std::string status_text;
        
        if (any_heroes && (dead_heroes == true))
        {
    	    status_text = get_text("heroes_dead");
            if (status_text == "")
                status_text = k_text_dead_heroes_ineligible;
    		ok_dialog( status_text, get_dialog_bottom( point ) );
        }
        // There are heroes, but they've all visited, or none has upgradable skills...
        else if (heroes.size() > 0)
        {
            status_text = get_text ("Empty");
	        ok_dialog( status_text, get_dialog_bottom( point ) );
        }
        // No heroes, show app. message...
        else
        {
            status_text = get_text ("need_hero");
	        ok_dialog( status_text, get_dialog_bottom( point ) );
        }
		play_music( music );
        return;
    }
    
    /*
    ** If at least one hero in the party is eligible for a skill upgrade, show the
    ** hero(es) dialog here, and allow the user to pick the hero(es) for skills upgrade.
    */
	std::vector<t_hero*>    selected_heroes;
    
    // Get selected heroes to upgrade.
	selected_heroes = select_heroes_dialog ( heroes, selectable_heroes, frame, army);

    /*
    ** No heroes selected - this check may be unnecessary, since dialog will return
    ** if no hero selected.
    */
    if (selected_heroes.size() == 0)
	{
		play_music( music );
        return;
	}
    
    /*
    ** Upgrade hero skills, and show the result of skill upgrade.
    */
    t_counted_ptr<t_basic_dialog> result_dialog_ptr;

    result_dialog_ptr = new t_basic_dialog;
    result_dialog_ptr->set_text( get_text ("paid") ); //Accept") );

    for (i=0; i < selected_heroes.size(); i++)
    {
        upgradable_skills = get_upgradable_skills(selected_heroes[i]);

        // Get random index.
        random_skill_index = random( 0, (upgradable_skills.size() - 1) );
        
        // Now do skill upgrade.
    	selected_heroes[i]->learn_skill ( upgradable_skills[random_skill_index], true );
        
        // Add to dialog, to show upgraded hero skill.
        show_upgraded_hero_skill( selected_heroes[i], upgradable_skills[random_skill_index], result_dialog_ptr );

        // Set hero to be *visited*.
	    selected_heroes[i]->set_visited( *this );
    }
    
    result_dialog_ptr->add_ok_button();
    result_dialog_ptr->open();
    result_dialog_ptr->run_modal();
	play_music( music );
}

// ------------------------------------------------------------------------
// Show help.
// ------------------------------------------------------------------------
void t_war_institute::right_click( t_mouse_event const& event,
	                                 t_adventure_frame* adventure_frame )
{
	std::string help_text;

	if (visited())
		//help_text = get_text( "help_visited" );
		help_text = get_text( "help" );
	else
		help_text = get_text( "help" );
	show_popup_text( help_text, event.screen_point );
}

// -------------------------------------------------------------
// Return upgradable skills.
// -------------------------------------------------------------
std::vector<t_skill> t_war_institute::get_upgradable_skills( t_hero* hero ) const
{
	std::vector<t_skill> hero_skills = hero->get_upgradable_skills();
	std::vector<t_skill> result;

	result.reserve(hero_skills.size());

	t_skill_set const * const skill_sets[] = {&m_preferred_skills, &m_available_skills};

	int i;

	for (i = 0; i < ELEMENTS_OF(skill_sets); i++)
	{
		t_skill_set const & skill_set = *skill_sets[i];

		std::vector<t_skill>::const_iterator it;
		for (it = hero_skills.begin(); it != hero_skills.end(); it++)
		{
			if (skill_set[it->skill])
			{
				result.push_back(*it);
			}
		}

		if (!result.empty())
			break; // Don't add available skills if we found a preferred skill
	}

	return result;
}

// -------------------------------------------------------------
// Does the hero have at least one upgradable skill?
// -------------------------------------------------------------
bool t_war_institute::has_upgradable_skills( t_hero* hero ) const
{
	std::vector<t_skill> hero_skills = hero->get_upgradable_skills();

	assert((m_preferred_skills | m_available_skills) == m_available_skills); // All preferred skills are available

	std::vector<t_skill>::const_iterator it;
	for (it = hero_skills.begin(); it != hero_skills.end(); it++)
	{
		if (m_available_skills[it->skill])
		{
			return true;
		}
	}

	return false;
}
// -------------------------------------------------------------
// Show hero select dialog.
// -------------------------------------------------------------
std::vector<t_hero*> t_war_institute::select_heroes_dialog( std::vector<t_hero*> const& heroes, 
														    std::vector<t_hero*> const& selectable_heroes,
															t_window* parent, t_army* army )
{
	t_counted_ptr<t_dialog_war_institute> dialog_ptr;

	dialog_ptr = new t_dialog_war_institute( parent );
	dialog_ptr->init_dialog( heroes, selectable_heroes, *this,
							 parent, army);
	dialog_ptr->run_modal();
            
    return (dialog_ptr->get_selected_heroes());
}

// -------------------------------------------------------------
// Needed for t_single_use_object derivation; we handle
// visits ourselves, though
// -------------------------------------------------------------
void t_war_institute::visit( t_hero* hero )
{

}

// -------------------------------------------------------------
// Version of war institue (save file)
// -------------------------------------------------------------
int t_war_institute::get_version() const
{
	return 1;
}

// -------------------------------------------------------------
// Read from save file
// -------------------------------------------------------------
bool t_war_institute::read( std::streambuf&						stream, 
						    t_qualified_adv_object_type const&	type,
						    int									version )
{
	if (version < 0 || version > 1)
		return false;

	int single_use_version;

	if (version >= 1)
	{
		single_use_version = get<int>( stream );
		if (!m_available_skills.read( stream )||
		    !m_preferred_skills.read( stream ))
		{
			return false;
		}
	}
	else
	{
		// leave defaults from constructor for skill sets
		single_use_version = version;
	}
	
	if (!t_single_use_object::read(stream, type, single_use_version))
		return false;

	
	return true;
}

// -------------------------------------------------------------
// Read from new map
// -------------------------------------------------------------
bool t_war_institute::read_from_map( std::streambuf& stream, t_progress_handler* progress)
{
	int version = get< t_uint16 >( stream );
	if ( version < 0 || version > k_current_map_format_version )
		return false;

	if (version >= 1)
	{
		if (!m_available_skills.read( stream )||
		    !m_preferred_skills.read( stream ))
		{
			return false;
		}
	}
	// else leave defaults from constructor

	return true;
}

// -------------------------------------------------------------
// Write to save file
// -------------------------------------------------------------
bool t_war_institute::write( std::streambuf& stream ) const
{
	put<int>( stream, t_single_use_object::get_version() );

	if (!m_available_skills.write(stream ) ||
		!m_preferred_skills.write( stream ))
	{
		return false;
	}

	return t_single_use_object::write(stream);
}

// -------------------------------------------------------------
// -------------------------------------------------------------
float t_war_institute::ai_value( t_adventure_ai const& ai, t_creature_array const& army, int move_cost ) const
{
	if (!player_can_afford( &army, k_skill_cost )) 
		return 0.0f;
	
	return 
		( ai_average_of_upgradable_skill_value( k_skill_cost, k_skill_tactics, k_skill_nobility, &army, this )
		 + ai_average_of_upgradable_skill_value( k_skill_cost, k_skill_offense, k_skill_diplomacy, &army, this ) ) 
		 / 2;
}

// -------------------------------------------------------------
// -------------------------------------------------------------
float t_war_institute::ai_value_to_hero( t_hero const* hero, t_creature_array const* array ) const
{
	return	
		( ai_average_of_upgradable_skill_value( hero, k_skill_cost, k_skill_tactics, k_skill_nobility, array, this )
			+ ai_average_of_upgradable_skill_value( hero, k_skill_cost, k_skill_offense, k_skill_diplomacy, array, this ) ) 
			/ 2;
}
